#include "Tower.h"
#include "ConfigManager.h"
#include "EnemyManager.h"
#include "BulletManager.h"

Tower::Tower()
{
    timerFire.SetOneShot(true);
    timerFire.SetOnTimeout([&]() { canFire = true; });

    animIdleUp.SetLoop(true);
    animIdleUp.SetInterval(0.2);
    animIdleDown.SetLoop(true);
    animIdleDown.SetInterval(0.2);
    animIdleLeft.SetLoop(true);
    animIdleLeft.SetInterval(0.2);
    animIdleRight.SetLoop(true);
    animIdleRight.SetInterval(0.2);

    animFireUp.SetLoop(false);
    animFireUp.SetInterval(0.2);
    animFireUp.SetOnFinished([&]() { UpdateIdleAnimation(); });

    animFireDown.SetLoop(false);
    animFireDown.SetInterval(0.2);
    animFireDown.SetOnFinished([&]() { UpdateIdleAnimation(); });

    animFireLeft.SetLoop(false);
    animFireLeft.SetInterval(0.2);
    animFireLeft.SetOnFinished([&]() { UpdateIdleAnimation(); });

    animFireRight.SetLoop(false);
    animFireRight.SetInterval(0.2);
    animFireRight.SetOnFinished([&]() { UpdateIdleAnimation(); });
}


void Tower::SetPosition(const Vector2 &position)
{
    this->position = position;
}

const Vector2 &Tower::GetSize() const
{
    return size;
}

const Vector2 &Tower::GetPosition() const
{
    return size;
}

void Tower::OnUpdate(double delta)
{
    timerFire.OnUpdate(delta);
    animCurrent->OnUpdate(delta);

    if (canFire) OnFire();
}

void Tower::OnRender(SDL_Renderer *renderer)
{
    static SDL_Point point;

    point.x = (int) (position.x - size.x / 2);
    point.y = (int) (position.y - size.y / 2);

    animCurrent->OnRender(renderer, point);
}

void Tower::UpdateIdleAnimation()
{
    switch (facing) {
        case Left:
            animCurrent = &animIdleLeft;
            break;
        case Right:
            animCurrent = &animIdleRight;
            break;
        case Up:
            animCurrent = &animIdleUp;
            break;
        case Down:
            animCurrent = &animIdleDown;
            break;
    }
}

void Tower::UpdateFireAnimation()
{
    switch (facing) {
        case Left:
            animCurrent = &animFireLeft;
            break;
        case Right:
            animCurrent = &animFireRight;
            break;
        case Up:
            animCurrent = &animFireUp;
            break;
        case Down:
            animCurrent = &animFireDown;
            break;
    }
}

Enemy *Tower::FindTargetEnemy()
{
    double process = -1;
    double view_range = 0;
    Enemy *enemyTarget = nullptr;

    static ConfigManager &instance = ConfigManager::Instance();

    switch (towerType) {
        case Archer:
            view_range = instance.archer_template.view_range[instance.level_archer];
            break;
        case Axeman:
            view_range = instance.axeman_template.view_range[instance.level_axeman];
            break;
        case Gunner:
            view_range = instance.gunner_template.view_range[instance.level_gunner];
            break;
    }

    std::vector<Enemy*> &enemyList = EnemyManager::Instance().GetEnemyList();

    for (Enemy *enemy: enemyList) {
        if ((enemy->GetPosition() - position).Length() <= view_range * SIZE_TILE) {
            double newProcess = enemy->GetRouterProcess();
            if (newProcess > process) {
                enemyTarget = enemy;
                process = newProcess;
            }
        }
    }

    return enemyTarget;
}

void Tower::OnFire()
{
    Enemy *targetEnemy = FindTargetEnemy();

    if (!targetEnemy) return;

    canFire = false;
    static ConfigManager &instance = ConfigManager::Instance();
    static const ResourcesManager::SoundPool &sound_pool = ResourcesManager::Instance().soundPool;

    double interval = 0, damage = 0;
    switch (towerType) {
        case Archer:
            interval = instance.archer_template.interval[instance.level_archer];
            damage = instance.archer_template.damage[instance.level_archer];
            switch (rand() % 2) {
                case 0:
                    Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ArrowFire_1)->second, 0);
                    break;
                case 1:
                    Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ArrowFire_2)->second, 0);
                    break;
            }
            break;
        case Axeman:
            interval = instance.axeman_template.interval[instance.level_axeman];
            damage = instance.axeman_template.damage[instance.level_axeman];
            Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_AxeFire)->second, 0);
            break;
        case Gunner:
            interval = instance.gunner_template.interval[instance.level_gunner];
            damage = instance.gunner_template.damage[instance.level_gunner];
            Mix_PlayChannel(-1, sound_pool.find(ResID::Sound_ShellFire)->second, 0);
            break;
    }
    timerFire.SetWaitTime(interval);
    timerFire.Restart();

    Vector2 direction = targetEnemy->GetPosition() - position;
    BulletManager::Instance().FireBullet(bulletType, position, direction.Normalize() * fireSpeed * SIZE_TILE,
                                           damage);

    bool is_show_x_anim = abs(direction.x) >= abs(direction.y);
    if (is_show_x_anim)
        facing = direction.x > 0 ? Facing::Right : Facing::Left;
    else
        facing = direction.y > 0 ? Facing::Down : Facing::Up;

    UpdateFireAnimation();
    animCurrent->Reset();
}